﻿using Sustainalytics.ClientDW.Entities;
using Sustainalytics.DataLayer.Shared;
using Sustainalytics.Utils;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Sustainalytics.ESGRatings.BusinessLogic.ESGOnlineReport
{

   
    public class HistoricalDataBusinessLogicUnit : IHistoricalDataBusinessLogicUnit
    {
        private static DateTime _referenceDate;
        private readonly IRepositoryCollection<CompanyHistoricalPerformance> _repository;
        private IRepositoryCollection<CorporateDataBasic> _corporateDataRepository;

        /// <exception cref="ArgumentNullException">The value of 'repository' cannot be null. </exception>
        public HistoricalDataBusinessLogicUnit(IRepositoryCollection<CompanyHistoricalPerformance> repository,  DateTime? refereceDate = null)
        {
            if (repository == null)
            {
                throw new ArgumentNullException("repository");
            }
            _repository = repository;
           
            _referenceDate = refereceDate ?? GetReferenceDate();
        }

        public void SetCorporateDataRepositorty(IRepositoryCollection<CorporateDataBasic> corporateDataRepository)
        {
            _corporateDataRepository = corporateDataRepository;
        }


        public Dictionary<Guid, CompanyHistoricalPerformance> GetByIds(IEnumerable<Guid> companyIds)
        {
            var history = _repository.ReadbyIds(companyIds);

            var ordered = history.Values.ToDictionary(x => x.Id, k => GetFilledCompanyHistorycalPerformance(k.Id, k));

            return ordered;
        }

        /// <exception cref="ESGReasearchNotInUniversesException">Not in universe. </exception>
        /// <exception cref="ESGReasearchNotFoundException">Not found. </exception>
        public CompanyHistoricalPerformance GetSingleByCompanyId(Guid companyId, IEnumerable<int> universeIds=null)
        {

            if (universeIds != null && _corporateDataRepository != null)
            {
                var corporateData = _corporateDataRepository.ReadWhere(x => x.Id == companyId, 0, 1,null, x => x.UniverseIds).FirstOrDefault();

                if (corporateData == null || !corporateData.UniverseIds.Intersect(universeIds).Any())
                    throw new ESGReasearchNotInUniversesException();
            }

            var history = _repository.Read(companyId);
            if (history == null)
                throw new ESGReasearchNotFoundException();

            return GetFilledCompanyHistorycalPerformance(companyId, history);         
        }

        private Action<CompanyHistoryData, CompanyHistoryData> FillPolicy()
        {
            return (prev, item) =>
            {
                if ((prev != null))
                {
                    item.CompanyId = prev.CompanyId;
                    if (item.EnvironmentAverage < 0 || item.EnvironmentAverage.Equals(Double.NaN)) item.EnvironmentAverage = prev.EnvironmentAverage;
                    if (item.EnvironmentBestInClass < 0 || item.EnvironmentBestInClass.Equals(Double.NaN)) item.EnvironmentBestInClass = prev.EnvironmentBestInClass;
                    if (item.EnvironmentScore < 0 || item.EnvironmentScore.Equals(Double.NaN)) item.EnvironmentScore = prev.EnvironmentScore;
                    if (item.SocialAverage < 0 || item.SocialAverage.Equals(Double.NaN)) item.SocialAverage = prev.SocialAverage;
                    if (item.SocialBestInClass < 0 || item.SocialBestInClass.Equals(Double.NaN)) item.SocialBestInClass = prev.SocialBestInClass;
                    if (item.SocialScore < 0 || item.SocialScore.Equals(Double.NaN)) item.SocialScore = prev.SocialScore;
                    if (item.GovernanceAverage < 0 || item.GovernanceAverage.Equals(Double.NaN)) item.GovernanceAverage = prev.GovernanceAverage;
                    if (item.GovernanceBestInClass < 0 || item.GovernanceBestInClass.Equals(Double.NaN)) item.GovernanceBestInClass = prev.GovernanceBestInClass;
                    if (item.GovernanceScore < 0 || item.GovernanceScore.Equals(Double.NaN)) item.GovernanceScore = prev.GovernanceScore;
                    if (item.OverallBestInClass < 0 || item.OverallBestInClass.Equals(Double.NaN)) item.OverallBestInClass = prev.OverallBestInClass;
                    if (item.OverallAverage < 0 || item.OverallAverage.Equals(Double.NaN)) item.OverallAverage = prev.OverallAverage;
                    if (item.OverallScore < 0 || item.OverallScore.Equals(Double.NaN)) item.OverallScore = prev.OverallScore;
                }
            };
        }

        private IEnumerable<CompanyHistoryData> GetDefault(Guid companyId, DateTime refDate)
        {
            return Enumerable.Range(-35, 36).Select(x => CompanyHistoryData.InitDefault(companyId, refDate.AddMonths(x)));
        }

        private CompanyHistoricalPerformance GetFilledCompanyHistorycalPerformance(Guid companyId,
            CompanyHistoricalPerformance history)
        {

            var defaultList = GetDefault(companyId, _referenceDate);
            var scoringList = (from defaultItem in defaultList
                               join item in history.Last36Months
                               on defaultItem.Date equals item.Date into gj
                               from i in gj.DefaultIfEmpty()
                               select i ?? defaultItem).ToList();

            scoringList.PrevCurrentZip(FillPolicy());


            return new CompanyHistoricalPerformance
            {
                Id = companyId,
                Last36Months = scoringList
            };
        }

        private DateTime GetReferenceDate()
        {
            return new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1, 0, 0, 0, 0, DateTimeKind.Utc);
        }
    }
}